﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MachineQA.Model;
using MachineQA.DataAccess;

namespace MachineQA.DataAccess
{
    public class MachineRepository : IMachineRepository
    {
        readonly List<Machine> _machines;
        readonly QADatabaseEntities _entities;

        /// <summary>
        /// Creates an instance of a machine repository.
        /// </summary>
        /// <param name="entityModel">An entity model to bind the repository to.</param>
        public MachineRepository(ref QADatabaseEntities entityModel)
        {
            _machines = entityModel.Machines.ToList();
            _entities = entityModel;
        }

        #region Public Interface

        /// <summary>
        /// Event that is raised if a machine is added or removed from the repository.
        /// </summary>
        public event EventHandler<MachineAddedOrRemovedEventArgs> MachineAddedOrRemoved;
        
        /// <summary>
        /// Adds a machine to the repository.
        /// </summary>
        /// <param name="machine">The machine to add.</param>
        public void AddMachine(Machine machine)
        {
            if (!_machines.Contains(machine))
            {
                _machines.Add(machine);
                _entities.AddToMachines(machine);
                _entities.SaveChanges(); // persist to db.
                if (this.MachineAddedOrRemoved != null)
                    this.MachineAddedOrRemoved(this, new MachineAddedOrRemovedEventArgs(machine, true));
            }
        }

        /// <summary>
        /// Removes the machine from the repository.
        /// </summary>
        /// <param name="machine">The machine to remove.</param>
        public void RemoveMachine(Machine machine)
        {
            if (_machines.Contains(machine))
            {
                _machines.Remove(machine);
                _entities.DeleteObject(machine);
                _entities.SaveChanges(); // persist to db.
                if (this.MachineAddedOrRemoved != null)
                    this.MachineAddedOrRemoved(this, new MachineAddedOrRemovedEventArgs(machine, false));
            }
        }

        /// <summary>
        /// Returns a shallow copied list of all the machines.
        /// </summary>
        /// <returns>A list containing the machines.</returns>
        public List<Machine> GetMachines()
        {
            return new List<Machine>(_machines);
        }

        public event EventHandler<MachineEnergiesModifiedEventArgs> MachineEnergiesModified;

        /// <summary>
        /// Adds an energy to the specified machine.
        /// </summary>
        /// <param name="machine">The machine to add the energy to.</param>
        /// <param name="energy">The energy to add.</param>
        public void AddMachineEnergy(Machine machine, Energy energy)
        {
            // Check args exist in their respective entity collections
            if(!_machines.Contains(machine))
                throw new ArgumentException(Properties.Resources.AddingEnergyToNonExistentMachineArgumentException);
            if(!_entities.Energies.Contains(energy))
                throw new ArgumentException(Properties.Resources.AddingNonExistentEnergyArgumentException);

            // Add the new energy if it isn't in the list already
            if (!_machines.Find((i) => i == machine).Energies.Contains(energy))
            {
                _machines.First((i) => i == machine).Energies.Add(energy);
                _entities.SaveChanges(); // persist to db

                if (this.MachineEnergiesModified != null)
                    this.MachineEnergiesModified(this, new MachineEnergiesModifiedEventArgs(machine, 
                        new List<Energy> { energy }, new List<Energy>()));
            }
        }

        /// <summary>
        /// Removes an energy from the specified machine.
        /// </summary>
        /// <param name="machine">The machine to remove the energy from.</param>
        /// <param name="energy">The energy to remove.</param>
        public void RemoveMachineEnergy(Machine machine, Energy energy)
        {
            // Check args exist in their respective entity collections
            if (!_machines.Contains(machine))
                throw new ArgumentException(Properties.Resources.RemovingEnergyFromNonExistentMachineArgumentException);
            if (!_entities.Energies.Contains(energy))
                throw new ArgumentException(Properties.Resources.RemovingNonExistentEnergyFromMachineArgumentException);

            // Remove the new energy if it is in the list already
            if (_machines.First((i) => i == machine).Energies.Contains(energy))
            {
                _machines.First((i) => i == machine).Energies.Remove(energy);
                _entities.SaveChanges(); // persist to db

                if (this.MachineEnergiesModified != null)
                    this.MachineEnergiesModified(this, new MachineEnergiesModifiedEventArgs(machine,
                        new List<Energy>(), new List<Energy> {energy}));
            }
        }

        /// <summary>
        /// Determines if the machine is contained in the repository.
        /// </summary>
        /// <param name="machine">The machine to check for.</param>
        /// <returns>The whether or not the machine is found.</returns>
        public bool ContainsMachine(Machine machine)
        {
            return _machines.Contains(machine);
        }

        #endregion // Public Interface
    }
}
